home *** CD-ROM | disk | FTP | other *** search
/ ftp.qualcomm.com / 2014.06.ftp.qualcomm.com.tar / ftp.qualcomm.com / eudora / developers / emsapi / carbon_emsapi.sit.hqx / Macintosh API Support / file1847.c < prev    next >
Text File  |  2001-03-08  |  22KB  |  720 lines

  1. /* ======================================================================
  2.  
  3.     Functions to manage MIME type data structures for use
  4.                  with Eudora translation API on the Mac.
  5.  
  6.     Filename:            file1847.c
  7.     Last Edited:        March 7, 1997
  8.     Authors:            Scott Manjourides
  9.     Copyright:            1995, 1996 QUALCOMM Inc.
  10.     Technical support:    <emsapi-info@qualcomm.com>
  11. */
  12.  
  13. #include <string.h>
  14. #include "emsapi-mac.h"
  15. #include "mimetype.h"
  16. #include "ctencode.h"
  17. #include "rfc822.h"
  18. #include "rfc1847.h"
  19. #include "file1847.h"
  20.  
  21. /* ------------------------------------------------------------------------ */
  22.  
  23. typedef struct {
  24.     Boolean            bFoundHeader;
  25.     BufTypeHandle    bufH, searchBufH;
  26.     TrEncType        cte;
  27.     unsigned long    nPrevEndMatch;
  28.     char            *preEncBuffer;
  29.     Dec64Ptr        d64state;
  30.     DecQPPtr        dQPstate;
  31. } PartParseState, *PartParseStatePtr, **PartParseStateHandle;
  32.  
  33. /* ------------------------------------------------------------------------ */
  34.  
  35. static Boolean    DoCreatePartsOutput(emsMIMEtypeH mimeHdl,
  36.                     BufTypeHandle inPart1H, BufTypeHandle inPart2H,
  37.                     createStateHandle hState, short outRef);
  38.                 
  39. static Boolean    DoCreateStringOutput(Handle strH, short part, emsMIMEtypeH mimeHdl,
  40.                     createStateHandle hState, short outRef);
  41.  
  42. static long        DoCreateOnePart(emsMIMEtypeH inPartMimeH, emsMIMEtypeH outMimeH,
  43.                     TrEncType inPartCTE, short fInRef, short fOutRef,
  44.                     short part, long preLen, long totalInLen,
  45.                     createStateHandle hState, emsProgress progressUPP);
  46.  
  47. static Boolean    DoParsePart(BufTypeHandle inBufH, short outRef, emsMIMEtypeH *mimeHdl,
  48.                     Boolean deMime, PartParseStatePtr pState);
  49.  
  50. static PartParseStatePtr CreatePartParseState();
  51. static void        DeletePartParseState(PartParseStatePtr p);
  52. static void        DoBufOutput(short refNum, BufTypeHandle pBuf);
  53.  
  54. /* ------------------------------------------------------------------------ */
  55.  
  56. #define kBufferSize (1024)
  57.  
  58. const StringPtr kMimeVersionStr = "\pMime-Version: 1.0\r\n";
  59.  
  60.  
  61. /* ======================================================================
  62.  *  RFC1847_FileCreate        Creates RFC1847 MIME structure.
  63.  *
  64.  *  Args:
  65.  *    outSpec        [IN]     Output FSSpec
  66.  *    outMimeH       [IN/OUT] MIME type of output -- boundary param will be added
  67.  *    inPart1MimeH   [IN]     MIME type of part 1
  68.  *    part1CTE       [IN]     Content-transfer-encoding for part 1
  69.  *    inPart1Spec    [IN]     Input FSSpec for part 1 ** READ-ONLY **
  70.  *    inPart2MimeH   [IN]     MIME type of part 2
  71.  *    part2CTE       [IN]     Content-transfer-encoding for part 2
  72.  *    inPart2Spec    [IN]     Input FSSpec for part 2 ** READ-ONLY **
  73.  *    progressUPP    [IN]     EMS progress callback procPtr
  74.  *
  75.  *  Returns status.
  76.  */
  77. long RFC1847_FileCreate(const FSSpec *outSpec, emsMIMEtypeH outMimeH,
  78.             emsMIMEtypeH inPart1MimeH, TrEncType part1CTE, const FSSpec *inPart1Spec,
  79.             emsMIMEtypeH inPart2MimeH, TrEncType part2CTE, const FSSpec *inPart2Spec,
  80.             emsProgress progressUPP)
  81. {
  82.     short                outRefNum, in1RefNum, in2RefNum;
  83.     long                len, in1Len, in2Len, totalLen, status = FILE1847_OK;
  84.     OSErr                err;
  85.     createStateHandle    hState;
  86.  
  87.     err = FSpOpenDF(inPart1Spec, fsRdPerm, &in1RefNum);        // input file
  88.     err = FSpOpenDF(inPart2Spec, fsRdPerm, &in2RefNum);        // signature
  89.     err = FSpOpenDF(outSpec, fsRdWrPerm, &outRefNum);        // output
  90.     if (err != noErr)
  91.         status = FILE1847_FAIL;
  92.  
  93.     len = StrLength(kMimeVersionStr);
  94.     err = FSWrite(outRefNum, &len, kMimeVersionStr + 1);
  95.  
  96.     if (progressUPP) {
  97.         err = GetEOF(in1RefNum, &in1Len);
  98.         err = GetEOF(in2RefNum, &in2Len);
  99.         totalLen = in1Len + in2Len;
  100.     }
  101.  
  102.     hState = NewCreateState();
  103.     if (!hState)
  104.         status = FILE1847_FAIL;
  105.  
  106.     if (status == FILE1847_OK)        // ---------- PART 1 ----------
  107.         status = DoCreateOnePart(inPart1MimeH, outMimeH, part1CTE, in1RefNum, outRefNum,
  108.                         1, 0, totalLen, hState, progressUPP);
  109.     
  110.     if (status == FILE1847_OK)        // ---------- PART 2 ----------
  111.         status = DoCreateOnePart(inPart2MimeH, outMimeH, part2CTE, in2RefNum, outRefNum,
  112.                         2, in1Len, totalLen, hState, progressUPP);
  113.  
  114.     if (status == FILE1847_OK) {    // Do 1847 cleanup
  115.         if (!DoCreatePartsOutput(outMimeH, nil, nil, hState, outRefNum))
  116.             status = FILE1847_FAIL;
  117.     }
  118.  
  119.     // Do final progress indicator
  120. //    if ((!status) && (progressUPP))
  121. //        status = (progress(100) ? 1 : 0);
  122.  
  123.     // Done with the state data
  124.     DeleteCreateState(hState);
  125.  
  126.     err = FSClose(outRefNum);
  127.     err = FSClose(in1RefNum);
  128.     err = FSClose(in2RefNum);
  129.     return status;
  130. }
  131.  
  132.  
  133. /* ======================================================================
  134.  *  RFC1847_FileParse        Parses RFC1847 MIME structure.
  135.  *
  136.  *  Args:
  137.  *    inSpec         [IN]      Input FSSpec of file to parse ** READ-ONLY **
  138.  *    inMimeH         [OUT]      MIME type of input file
  139.  *    outPart1Spec     [IN]      Output FSSpec for part 1 ** WRITE-ONLY **
  140.  *    outPart1MimeH     [OUT]      MIME type of created part 1
  141.  *    part1DeMime     [IN]      Remove header and transfer encoding, part 1?
  142.  *    outPart2Spec     [IN]      Output FSSpec for part 2 ** WRITE-ONLY **
  143.  *    outPart2MimeH     [OUT]      MIME type of created part 2
  144.  *    part2DeMime     [IN]      Remove header and transfer encoding, part 2?
  145.  *    progressUPP     [IN]      EMS progress callback function
  146.  *
  147.  *  Returns status.
  148.  *  The files names passed in cannot be nil, and must exist.
  149.  */
  150. long RFC1847_FileParse(const FSSpec *inSpec, emsMIMEtypeH *inMimeH,
  151.             const FSSpec *outPart1Spec, emsMIMEtypeH *outPart1MimeH, Boolean part1DeMime,
  152.             const FSSpec *outPart2Spec, emsMIMEtypeH *outPart2MimeH, Boolean part2DeMime,
  153.             emsProgress progressUPP)
  154. {
  155.     short                inRefNum, out1RefNum, out2RefNum;
  156.     Boolean                bAbort = false;
  157.     long                percentComplete, filePos, eofPos, len, ret, status = FILE1847_OK;
  158.     Boolean                reallyParse1, reallyParse2;
  159.     BufTypeHandle        outPart1Buf, outPart2Buf, inBuf;
  160.     parseStateHandle    hState;
  161.     PartParseStatePtr    pParsePart1State, pParsePart2State;
  162.     emsProgressData        progData = { sizeof(emsProgressData), 0, nil };
  163.     Handle                h;
  164.     OSErr                err;
  165.  
  166.     inRefNum = out1RefNum = out2RefNum = -1;
  167.     if (FSpOpenDF(inSpec, fsRdPerm, &inRefNum) != noErr)
  168.         return FILE1847_FAIL;
  169.     if (FSpOpenDF(outPart1Spec, fsRdWrPerm, &out1RefNum) != noErr) {
  170.         status = FILE1847_FAIL;
  171.         goto Exit;
  172.     }
  173.     if (FSpOpenDF(outPart2Spec, fsRdWrPerm, &out2RefNum) != noErr) {
  174.         status = FILE1847_FAIL;
  175.         goto Exit;
  176.     }
  177.  
  178.     outPart1Buf = MakeSize_Buf(kBufferSize);
  179.     outPart2Buf = MakeSize_Buf(kBufferSize);
  180.     inBuf = MakeSize_Buf(kBufferSize);
  181.     if (!outPart1Buf || !outPart2Buf || !inBuf) {
  182.         status = FILE1847_FAIL;
  183.         goto Exit;
  184.     }
  185.  
  186.     // Check to see if we need to parse the parts
  187.     reallyParse1 = reallyParse2 = FALSE;
  188.     if (outPart1MimeH || part1DeMime)
  189.         reallyParse1 = TRUE;
  190.     if (outPart1MimeH || part2DeMime)
  191.         reallyParse2 = TRUE;
  192.  
  193.     err = GetEOF(inRefNum, &eofPos);
  194.     if (progressUPP) {    // get the file length of the two input files -- use for progress
  195.         progData.value = 0;
  196.         if (CallEMSProgressProc(progressUPP, &progData)) {
  197.             status = FILE1847_ABORT;
  198.             goto Exit2;
  199.         }
  200.     }
  201.  
  202.     pParsePart1State = CreatePartParseState();
  203.     pParsePart2State = CreatePartParseState();
  204.     hState = NewParseState();
  205.  
  206.     if ((pParsePart1State) && (pParsePart2State) && (hState)) {
  207.         do {
  208.             /* Fill the input buffer from the input file */
  209.             EmptyBuf_Buf(inBuf);
  210.             len = BufSize_Buf(inBuf);
  211.             h = GetBuf_Buf(inBuf);
  212.             HLock(h);
  213.             err = FSRead(inRefNum, &len, *h);
  214.             HUnlock(h);
  215.             if (err != noErr && err != eofErr)
  216.                 break;
  217.             SetLen_Buf(inBuf, len);
  218.  
  219.             /* Parse the input, producing output until completed input */
  220.             do {
  221.                 EmptyBuf_Buf(outPart1Buf);
  222.                 EmptyBuf_Buf(outPart2Buf);
  223.  
  224.                 ret = RFC1847_Parse(inMimeH, outPart1Buf, outPart2Buf, inBuf, hState);
  225.                 if (ret != RFC1847_FAIL) {
  226.                     if (reallyParse1) {    /* Parse part #1 */
  227.                         if (!DoParsePart(outPart1Buf, out1RefNum, outPart1MimeH, part1DeMime, pParsePart1State))
  228.                             status = FILE1847_FAIL;
  229.                     }
  230.                     else /* Raw output part #1 */
  231.                         DoBufOutput(out1RefNum, outPart1Buf);
  232.  
  233.                     if (reallyParse2) {    /* Parse part #2 */
  234.                         if (!DoParsePart(outPart2Buf, out2RefNum, outPart2MimeH, part2DeMime, pParsePart2State))
  235.                             status = FILE1847_FAIL;
  236.                     }
  237.                     else /* Raw output part #2 */
  238.                         DoBufOutput(out2RefNum, outPart2Buf);
  239.                 }
  240.             } while ((status == FILE1847_OK) && (ret == RFC1847_BUFFERFULL));
  241.             
  242.             if (ret == RFC1847_FAIL)
  243.                 status = FILE1847_FAIL;
  244.  
  245.             err = GetFPos(inRefNum, &filePos);
  246.             if ((status == FILE1847_OK) && (progressUPP)) {
  247.                 percentComplete = (filePos * 100) / eofPos;
  248.                 progData.value = percentComplete;
  249.                 if (CallEMSProgressProc(progressUPP, &progData))
  250.                     status = FILE1847_ABORT;
  251.             }
  252.         } while ((status == FILE1847_OK) && (filePos < eofPos));
  253.  
  254.         if (status == FILE1847_OK) {
  255.             if (reallyParse1) /* Parse part #1 */
  256.                 if (!DoParsePart(nil, out1RefNum, outPart1MimeH, part1DeMime, pParsePart1State))
  257.                     status = FILE1847_FAIL;
  258.             if (reallyParse2) /* Parse part #2 */
  259.                 if (!DoParsePart(nil, out2RefNum, outPart2MimeH, part2DeMime, pParsePart2State))
  260.                     status = FILE1847_FAIL;
  261.         }
  262.     }
  263.     else
  264.         status = FILE1847_FAIL;
  265.     
  266.     DeletePartParseState(pParsePart1State);
  267.     DeletePartParseState(pParsePart2State);
  268.     DeleteParseState(hState);
  269. Exit2:
  270.     Delete_Buf(outPart1Buf);
  271.     Delete_Buf(outPart2Buf);
  272.     Delete_Buf(inBuf);
  273. Exit:
  274.     if (inRefNum != -1)
  275.         err = FSClose(inRefNum);
  276.     if (out1RefNum != -1)
  277.         err = FSClose(out1RefNum);
  278.     if (out2RefNum != -1)
  279.         err = FSClose(out2RefNum);
  280.     return status;
  281. }
  282.  
  283. #pragma mark -
  284. /* ------------------------------------------------------------------------ */
  285.  
  286. static Boolean DoCreatePartsOutput(emsMIMEtypeH mimeHdl,
  287.                    BufTypeHandle inPart1H, BufTypeHandle inPart2H,
  288.                    createStateHandle hState, short outRef)
  289. {
  290.     BufTypeHandle    outBufH;
  291.     unsigned long    outLen, status;
  292.     OSErr            err;
  293.     long            len;
  294.     Handle            h;
  295.  
  296.     if (inPart1H)
  297.         ResetPos_Buf(inPart1H);
  298.     if (inPart2H)
  299.         ResetPos_Buf(inPart2H);
  300.  
  301.     outBufH = MakeSize_Buf(kBufferSize);
  302.     do {
  303.         EmptyBuf_Buf(outBufH);        // Clear the output
  304.         status = RFC1847_Create(mimeHdl, outBufH, inPart1H, inPart2H, hState);
  305.         if ((outLen = BufLen_Buf(outBufH)) > 0) {    // Output whatever we got
  306.             h = GetBuf_Buf(outBufH);
  307.             HLock(h);
  308.             len = BufLen_Buf(outBufH);
  309.             err = FSWrite(outRef, &len, *h);    // + pos
  310.              HUnlock(h);
  311.             if (err) {
  312.                 status = RFC1847_FAIL;
  313.                 break;
  314.             }
  315.         }
  316.     } while (status == RFC1847_BUFFERFULL);
  317.  
  318.     Delete_Buf(outBufH);
  319.     return (status != RFC1847_FAIL);
  320. }
  321.  
  322.  
  323. /* ------------------------------------------------------------------------ */
  324.  
  325. static Boolean DoCreateStringOutput(Handle strH, short part, emsMIMEtypeH mimeHdl,
  326.                    createStateHandle hState, short outRef)
  327. {
  328.     BufTypeHandle    bufH;
  329.     long            status = RFC1847_FAIL;
  330.  
  331.     bufH = Make_Buf();
  332.     if (bufH) {
  333.         // Manually setup the buffer structure -- kind of ugly, but
  334.         // more efficient than copying the string
  335.         (**bufH).bufHandle = strH;
  336.         (**bufH).len = GetHandleSize(strH);
  337.         ResetPos_Buf(bufH);
  338.  
  339.         // Now output the buffer
  340.         if (part == 1)
  341.             status = DoCreatePartsOutput(mimeHdl, bufH, nil, hState, outRef);
  342.         else
  343.             status = DoCreatePartsOutput(mimeHdl, nil, bufH, hState, outRef);
  344.         Clear_Buf(bufH);    // Unassociate buffer with string
  345.         Delete_Buf(bufH);    // Delete buffer struct -- AFTER we clear it's contents
  346.     }
  347.     return (status != RFC1847_FAIL);
  348. }
  349.  
  350.  
  351. /* ------------------------------------------------------------------------ */
  352.  
  353. static long DoCreateOnePart(emsMIMEtypeH inPartMimeH, emsMIMEtypeH outMimeH,
  354.                      TrEncType inPartCTE, short fInRef, short fOutRef,
  355.                      short part, long preLen, long totalInLen,
  356.                      createStateHandle hState, emsProgress progressUPP)
  357. {
  358.     BufTypeHandle    inBufH;
  359.     Handle            hCT, hCTE, crlfH;
  360.     char            *preEncBuffer = nil;
  361.     unsigned long    preEncBufLen;
  362.     Enc64Ptr        e64state = nil; // Used by Encode64()
  363.     EncQPPtr        eQPstate = nil; // Used by EncodeQP()
  364.     long            status, filePos, eofPos, len, readLen;
  365.     short            failed;
  366.     OSErr            err;
  367.     emsProgressData    progData = { sizeof(emsProgressData), 0, nil };
  368.  
  369.     inBufH = MakeSize_Buf(kBufferSize);
  370.     if (!inBufH)
  371.         return FILE1847_FAIL;
  372.     
  373.     status = FILE1847_OK;
  374.     PtrToHand("\r\n", &crlfH, 2);
  375.  
  376.     // Do InPartMimePtr (Content-Type) header
  377.     if ((status == FILE1847_OK) && (inPartMimeH)) {
  378.         hCT = StringMimeType(inPartMimeH);
  379.         if (hCT) {
  380.             if (!DoCreateStringOutput(hCT, part, outMimeH, hState, fOutRef))
  381.                 status = FILE1847_FAIL;
  382.             else if (!DoCreateStringOutput(crlfH, part, outMimeH, hState, fOutRef))
  383.                 status = FILE1847_FAIL;
  384.             DisposeHandle(hCT);
  385.         }
  386.         else
  387.             status = FILE1847_FAIL;
  388.     }
  389.  
  390.     // Do inPartCTE (Content-Transfer-Encoding) header
  391.     if ((status == FILE1847_OK) && (inPartCTE != CTE_NONE)) {
  392.         hCTE = RFC822_MakeCTE(inPartCTE);
  393.         if (hCTE) {
  394.             if (!DoCreateStringOutput(hCTE, part, outMimeH, hState, fOutRef))
  395.                 status = FILE1847_FAIL;
  396.             else if (!DoCreateStringOutput(crlfH, part, outMimeH, hState, fOutRef))
  397.                 status = FILE1847_FAIL;
  398.             DisposeHandle(hCTE);
  399.         }
  400.         else
  401.             status = FILE1847_FAIL;
  402.     }
  403.  
  404.     // Do blank line -- only if we put out some header already
  405.     if ((status == FILE1847_OK) && ((inPartMimeH) || (inPartCTE != CTE_NONE))) {
  406.         if (!DoCreateStringOutput(crlfH, part, outMimeH, hState, fOutRef))
  407.             status = FILE1847_FAIL;
  408.     }
  409.  
  410.     if (status == FILE1847_OK) {
  411.         switch (inPartCTE) {
  412.             case CTE_Base64:    // BASE64
  413.                 // BASE64 expands about 1/3
  414.                 preEncBufLen = ((kBufferSize * 6) / 10);
  415.                 preEncBuffer = NewPtr(preEncBufLen);
  416.                 if (!preEncBuffer)
  417.                     status = FILE1847_FAIL; // Could not create buffer
  418.  
  419.                 e64state = (Enc64Ptr) NewPtr(sizeof(Enc64)); // Used by Encode64()
  420.                 e64state->partialCount = e64state->bytesOnLine = 0;
  421.                 break;
  422.             case CTE_QP:        // QUOTED-PRINTABLE
  423.                 // QP expands max of 3 times
  424.                 preEncBufLen = (kBufferSize / 4);
  425.                 preEncBuffer = NewPtr(preEncBufLen);
  426.                 if (!preEncBuffer)
  427.                     status = FILE1847_FAIL; // Could not create buffer
  428.  
  429.                 eQPstate = (EncQPPtr) NewPtr(sizeof(EncQP)); // Used by EncodeQP()
  430.                 eQPstate->nCurLineLen = 0;
  431.                 eQPstate->cLastChar = '\0';
  432.                 break;
  433.             default:            // Otherwise, no encoding
  434.                 preEncBufLen = 0;
  435.         }
  436.     }
  437.  
  438.     // Do pInPartFilename
  439.     while (status == FILE1847_OK) {
  440.         EmptyBuf_Buf(inBufH);
  441.         Lock_Buf(inBufH);
  442.         switch (inPartCTE) {
  443.             case CTE_Base64:
  444.                 readLen = preEncBufLen;
  445.                 err = FSRead(fInRef, &readLen, preEncBuffer);
  446.                 len = preEncBufLen;
  447.                 failed = Encode64((UPtr) preEncBuffer, readLen,
  448.                                     (UPtr) *GetBuf_Buf(inBufH), &len, e64state);
  449.                 readLen = len;
  450.                 break;
  451.  
  452.             case CTE_QP:
  453.                 readLen = preEncBufLen;
  454.                 err = FSRead(fInRef, &readLen, preEncBuffer);
  455.                 len = preEncBufLen;
  456.                 failed = EncodeQP((UPtr) preEncBuffer, readLen,
  457.                                    (UPtr) *GetBuf_Buf(inBufH), &len, (long*) eQPstate);
  458.                 readLen = len;
  459.                 break;
  460.  
  461.             default: /* 7bit, 8bit, binary, none */
  462.                 readLen = BufSize_Buf(inBufH);
  463.                 err = FSRead(fInRef, &readLen, *GetBuf_Buf(inBufH));
  464.                 break;
  465.         }
  466.         Unlock_Buf(inBufH);
  467.  
  468.         if (readLen > 0) {
  469.             SetLen_Buf(inBufH, readLen);
  470.             if (part == 1) {
  471.                 if (!DoCreatePartsOutput(outMimeH, inBufH, nil, hState, fOutRef))
  472.                     status = FILE1847_FAIL;
  473.             }
  474.             else {
  475.                 if (!DoCreatePartsOutput(outMimeH, nil, inBufH, hState, fOutRef))
  476.                     status = FILE1847_FAIL;
  477.             }
  478.         }
  479.  
  480.         if (progressUPP) {    // Update the progress and check for abort
  481.             err = GetFPos(fInRef, &filePos);
  482.             progData.value = (((filePos + preLen) * 100L) / totalInLen);
  483.             if (CallEMSProgressProc(progressUPP, &progData)) {
  484.                 status = FILE1847_ABORT;
  485.                 goto Exit;
  486.             }
  487.         }
  488.  
  489.         err = GetFPos(fInRef, &filePos);
  490.         err = GetEOF(fInRef, &eofPos);
  491.         if (filePos >= eofPos)
  492.             break;
  493.     }
  494.  
  495.     // Finish part -- if needed
  496.     if (status == FILE1847_OK) {
  497.         switch (inPartCTE) {
  498.             case CTE_Base64:
  499.                 readLen = BufSize_Buf(inBufH);
  500.                 failed = Encode64(nil, 0, (UPtr) *GetBuf_Buf(inBufH), &readLen, e64state);
  501.                 break;
  502.             case CTE_QP:
  503.                 readLen = BufSize_Buf(inBufH);
  504.                 failed = EncodeQP(nil, 0, (UPtr) *GetBuf_Buf(inBufH), &readLen, (long*) eQPstate);
  505.                 break;
  506.             default: /* 7bit, 8bit, binary, none */
  507.                 readLen = 0;
  508.         }
  509.         if (readLen > 0) {
  510.             SetLen_Buf(inBufH, readLen);
  511.             if (part == 1) {
  512.                 if (!DoCreatePartsOutput(outMimeH, inBufH, nil, hState, fOutRef))
  513.                     status = FILE1847_FAIL;
  514.             }
  515.             else {
  516.                 if (!DoCreatePartsOutput(outMimeH, nil, inBufH, hState, fOutRef))
  517.                     status = FILE1847_FAIL;
  518.             }
  519.         }
  520.     }
  521. Exit:
  522.     if (e64state != nil)
  523.         DisposePtr((Ptr) e64state);
  524.     if (eQPstate != nil)
  525.         DisposePtr((Ptr) eQPstate);
  526.     if (preEncBuffer != nil)
  527.         DisposePtr(preEncBuffer);
  528.     Delete_Buf(inBufH);
  529.     DisposeHandle(crlfH);
  530.  
  531.     return status;
  532. }
  533.  
  534. /* ------------------------------------------------------------------------ */
  535.  
  536. static Boolean DoParsePart(BufTypeHandle inBufH, short outRef, emsMIMEtypeH *mimeHdl,
  537.                             Boolean deMime, PartParseStatePtr pState)
  538. {
  539.     unsigned short    nLen, newMatched;
  540.     short            failed;
  541.     char            *pHeader, *pCT, *pCTE;
  542.     long            len, errorcnt, nSkip, nRemain, preEncBufLen;
  543.     Handle            h;
  544.     OSErr            err;
  545.  
  546.     if (!inBufH) { // Input buf nil indicates 'cleanup'
  547.         return (pState->bFoundHeader);
  548.     }
  549.     if (!pState->bFoundHeader) {
  550.         ResetPos_Buf(pState->searchBufH);
  551.  
  552.         if (pState->nPrevEndMatch > 0) {    /* Check for completion of span */
  553.             // If doesn't continue match, returns zero
  554.             // otherwise returns number of chars of searchBufH that have been matched
  555.             newMatched = CompleteCount_Buf(inBufH, pState->searchBufH, pState->nPrevEndMatch);
  556.             if (newMatched == BufLen_Buf(pState->searchBufH)) { /* complete match made */
  557.                 pState->bFoundHeader = true;
  558.                 pState->nPrevEndMatch = 0;
  559.                 BufNCat_Buf(pState->bufH, inBufH, (BufLen_Buf(pState->searchBufH) - (pState->nPrevEndMatch)));
  560.             }
  561.             else if (newMatched != 0) {        /* Continued to match, but not completed yet -- the input buffer is smaller than the searchBufH */
  562.                 BufNCat_Buf(pState->bufH, inBufH, PosLen_Buf(inBufH));
  563.                 pState->nPrevEndMatch = newMatched;
  564.                 return true;
  565.             }
  566.             else {                            /* No match continuation */
  567.                 pState->nPrevEndMatch = 0;
  568.             }
  569.         }
  570.  
  571.         ResetPos_Buf(pState->searchBufH);
  572.  
  573.         // Still not found -- no span
  574.         if (!pState->bFoundHeader) {
  575.             // Find match of searchBufH, either complete or end-spanning
  576.             // return number of chars to skip before match
  577.             nSkip = SkipCount_Buf(inBufH, pState->searchBufH);
  578.             nRemain = PosLen_Buf(inBufH) - nSkip;
  579.  
  580.             if (nRemain == 0) { // Not found
  581.                 BufNCat_Buf(pState->bufH, inBufH, PosLen_Buf(inBufH));
  582.                 return true;
  583.             }
  584.             else if (nRemain > BufLen_Buf(pState->searchBufH)) { /* Found 'complete' */
  585.                 pState->bFoundHeader = true;
  586.                 BufNCat_Buf(pState->bufH, inBufH, (nSkip + BufLen_Buf(pState->searchBufH)));
  587.             }
  588.             else { // Partial possible
  589.                 pState->nPrevEndMatch = nRemain;
  590.                 BufNCat_Buf(pState->bufH, inBufH, PosLen_Buf(inBufH));
  591.                 return true;
  592.             }
  593.         }
  594.  
  595.         // ---------- Now we know it is found ----------
  596.  
  597.         nLen = BufLen_Buf(pState->bufH);
  598.         pHeader = NewPtr(nLen + 1);
  599.         strncpy(pHeader, *GetBuf_Buf(pState->bufH), nLen);
  600.  
  601.         pCT = RFC822_ExtractHeader(pHeader, kContentTypeHdrCStr);
  602.         
  603.         if (mimeHdl)
  604.             *mimeHdl = ParseMakeMimeType(pCT);
  605.  
  606.         if (deMime) {
  607.             pCTE = RFC822_ExtractCTE(pHeader);
  608.             if (pCTE)
  609.                 pState->cte = RFC822_ParseCTE(pCTE);
  610.             else
  611.                 pState->cte = CTE_NONE; /* No CTE header, so no encoding */
  612.  
  613.             switch (pState->cte) {
  614.                 case CTE_Base64:    // BASE64 expands about 1/3
  615.                     preEncBufLen = ((kBufferSize * 10) / 6);
  616.                     pState->preEncBuffer = NewPtr(preEncBufLen);
  617.                     pState->d64state = (Dec64Ptr) NewPtrClear(sizeof(Dec64)); // Used by Decode64()
  618.                     break;
  619.  
  620.                 case CTE_QP:        // QP expands max of 3 times
  621.                     preEncBufLen = (kBufferSize * 4);
  622.                     pState->preEncBuffer = NewPtr(preEncBufLen);
  623.                     pState->dQPstate = (DecQPPtr) NewPtrClear(sizeof(EncQP));
  624.                     pState->dQPstate->state = qpNormal;
  625.                     break;
  626.  
  627.                 // Otherwise, no encoding
  628.                 default:    // Otherwise, no encoding
  629.                     ;
  630.             }
  631.         }
  632.         else {
  633.             len = nLen;
  634.             h = GetBuf_Buf(pState->bufH);
  635.             HLock(h);
  636.             err = FSWrite(outRef, &len, *h);
  637.             HUnlock(h);
  638.         }
  639.  
  640.         Free_Buf(pState->bufH);
  641.     }
  642.  
  643.     // ---------- Now we know it is found and header is init'd ----------
  644.     Lock_Buf(inBufH);
  645.     switch (pState->cte) {
  646.         case CTE_Base64:
  647.             len = preEncBufLen;
  648.             failed = Decode64((UPtr) GetPos_Buf(inBufH), PosLen_Buf(inBufH),
  649.                         (UPtr) pState->preEncBuffer, &len, pState->d64state, &errorcnt);
  650.             err = FSWrite(outRef, &len, pState->preEncBuffer);
  651.             break;
  652.         case CTE_QP:
  653.             len = preEncBufLen;
  654.             failed = DecodeQP((UPtr) GetPos_Buf(inBufH), PosLen_Buf(inBufH),
  655.                         (UPtr) pState->preEncBuffer, &len, pState->dQPstate, &errorcnt);
  656.             err = FSWrite(outRef, &len, pState->preEncBuffer);
  657.             break;
  658.         default:    /* 7bit, 8bit, binary, none */
  659.             len = PosLen_Buf(inBufH);
  660.             err = FSWrite(outRef, &len, GetPos_Buf(inBufH));
  661.             break;
  662.     }
  663.     Unlock_Buf(inBufH);
  664.     return true;
  665. }
  666.  
  667. #pragma mark -
  668. /* ------------------------------------------------------------------------ */
  669.  
  670. static PartParseStatePtr CreatePartParseState(void)
  671. {
  672.     PartParseStatePtr    p;
  673.     
  674.     p = (PartParseStatePtr) NewPtr(sizeof(PartParseState));
  675.     if (p) {
  676.         p->bFoundHeader = false;
  677.         p->bufH = Make_Buf();
  678.         p->searchBufH = Make_Buf();
  679.         StrCpy_Buf(p->searchBufH, "\p\r\n\r\n");
  680.         p->cte = CTE_NONE;
  681.         p->nPrevEndMatch = 0;
  682.         p->preEncBuffer = nil;
  683.         p->d64state = nil;
  684.         p->dQPstate = nil;
  685.     }
  686.     return p;
  687. }
  688.  
  689. /* ------------------------------------------------------------------------ */
  690.  
  691. static void DeletePartParseState(PartParseStatePtr p)
  692. {
  693.     if (p) {
  694.         Delete_Buf(p->bufH);
  695.         Delete_Buf(p->searchBufH);
  696.         DisposePtr((Ptr) p->preEncBuffer);
  697.         DisposePtr((Ptr) p->d64state);
  698.         DisposePtr((Ptr) p->dQPstate);
  699.         DisposePtr((Ptr) p);
  700.     }
  701. }
  702.  
  703.  
  704. /* ======================================================================
  705.  *  DoBufOutput (static)
  706.  *
  707.  *  Private function used to output a buffer.
  708.  */
  709. static void DoBufOutput(short refNum, BufTypeHandle pBuf)
  710. {
  711.     long        len;
  712.     Handle        h;
  713.     
  714.     len = PosLen_Buf(pBuf);
  715.     h = GetBuf_Buf(pBuf);
  716.     HLock(h);
  717.     FSWrite(refNum, &len, *h);
  718.     HUnlock(h);
  719. }
  720.